package com.nxy.cpfsfe.busi.bbatproc;

import com.adtec.starring.datapool.CompSDO;
import com.adtec.starring.datapool.EPOper;
import com.adtec.starring.exception.BaseException;
import com.adtec.starring.struct.dta.DtaInfo;
import com.adtec.starring.util.SpringUtil;
import com.nxy.cpfsfe.ToParmMapResultHandler;
import com.nxy.cpfsfe.enumtype.DBErrorCdEnum;
import com.nxy.cpfsfe.enumtype.RespCd;
import com.nxy.cpfsfe.enumtype.TdBatchAEnum;
import com.nxy.cpfsfe.enumtype.TeBatchReqEnum;
import com.nxy.cpfsfe.exception.CpfsBizException;
import com.nxy.cpfsfe.mybatis.mapper.TdBatchAMapper;
import com.nxy.cpfsfe.mybatis.mapper.TeBatchReqMapper;
import com.nxy.cpfsfe.mybatis.mapper.ToOrgMapper;
import com.nxy.cpfsfe.mybatis.pojo.TdBatchA;
import com.nxy.cpfsfe.mybatis.pojo.TeBatchReq;
import com.nxy.cpfsfe.mybatis.pojo.ToOrg;
import com.nxy.cpfsfe.mybatis.pojo.ToParm;
import com.nxy.cpfsfe.mybatis.service.TeBatchReqService;
import com.nxy.cpfsfe.service.CpfsService;
import com.nxy.cpfsfe.util.*;
import com.nxy.cpfsfe.util.sftp.SFTPTransfer;
import com.nxy.cpfsfe.util.zookeeper.DistributedLock;
import com.nxy.cpfsfe.util.zookeeper.ZookeeperLockUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.io.File;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.*;

/**
 * 批量业务处理业务
 * @author lcq
 *
 */
public class BatStlSrvBean  implements CpfsService{
	private final static Logger LOGGER = LoggerFactory.getLogger(BatStlSrvBean.class);	
    private SqlSession mybatisSqlSession;
    /**
	 * 批量结算同步处理
     * @return 
     * @throws Exception 
	 */
    public  void doService() {
	   	//zookeeper锁定此批次信息
		DistributedLock distributedLock = null;
		Map<String,String> map = null;
		TeBatchReq tReq = null;
		//回执报文
		String returnMsg = null ;
    	try {
			LOGGER.info("---------批量结算同步处理开始--------");
			// 获取输入
			String tpId = DtaInfo.getInstance().getTpId();
			CompSDO inputSdo = (CompSDO) EPOper.getCompSDO(tpId, "ReqBody"); // 获取输入接口
			String bizMsg = (String) inputSdo.getValue("BizMsg")!=null?(String) inputSdo.getValue("BizMsg"):""; // 业务报文
			LOGGER.info("业务报文数据bizMsg:[{}]",bizMsg);
			//生成业务map
			map = XmlUtil.xml2Map(bizMsg);
			//检查报文，目前检查比较简略，之后由统一的报文检查模块进行检查
			bizMsgXmlCheck(map);
			String batNo = StringUtil.trimObject(map.get("/root/MsgBody/StlSumInfo/BatNo"));
			String sndSysId = StringUtil.trimObject(map.get("/root/MsgHeader/SndSysId"));
			LOGGER.debug("报文请求系统编号：[{}]，批次信息：[{}]",sndSysId,batNo);
			distributedLock = ZookeeperLockUtil.getInstance().creaZooKeeperLock( sndSysId+"TeBatchReq"+batNo);
			if(distributedLock.tryLock()){
				//获得锁并记录批次表信息、流失号与批次号也进行判断
				tReq = insertTeBatchReq(map);
				//1、通过sftp读取文件
				String sndBankId =map.get("/root/MsgHeader/SndBankId");
				String localfilepath =downLoadFileSFtp(sndBankId,tReq.getReqFileUrl());
				//获取文件信息
				List<String> datalists = FileUtil.readFile(localfilepath);
				//2、解析List，检查业务报文及文件格式
				List<TdBatchA> tALists = new  ArrayList<TdBatchA>();
				checkLists(datalists,tReq,tALists,map);
				LOGGER.debug("开始明细入库处理");
				//登记批量请求登记表信息
				batchDetailInsertReq(tALists);
				//设置成功返回信息
				map.put("resultCode", RespCd.NCPI0000);
				map.put("msg", "处理成功");
			}else{
				LOGGER.warn(CpfsBizException.LOCK_MSG);
				map.put("msg", CpfsBizException.LOCK_MSG);
				map.put("resultCode",RespCd.NCPO9004);
			}
			returnMsg =ResponseUtil.buildXmlGeneral(map);
		}catch(BaseException e){
			LOGGER.error("异常信息为errcode:[{}].错误信息[{}]",e.getErrorCode(),e.getErrorDesc());
			LOGGER.error("错误栈信息",e);
			map.put("resultCode", e.getErrorCode());
			map.put("msg", e.getErrorDesc());
			if(tReq != null){
				tReq.setProcSta(TeBatchReqEnum.ProcSta_Fail.getCode());
				tReq.setProcDesc(e.getErrorDesc());
				TeBatchReqService.updateTeBatchReqStatus(tReq);
			}
			returnMsg =ResponseUtil.buildXmlGeneral(map);

		}catch(Throwable e){
			LOGGER.error("未知异常信息栈",e);
			map.put("resultCode",RespCd.NCPS7001);
			map.put("msg", "未知异常，请联系相关人员查看");
            if(tReq != null){
                tReq.setProcSta(TeBatchReqEnum.ProcSta_Fail.getCode());
                tReq.setProcDesc("未知异常，请联系相关技术人员查看");
                TeBatchReqService.updateTeBatchReqStatus(tReq);
            }
			returnMsg =ResponseUtil.buildXmlGeneral(map);
		}finally{
			ResponseUtil.responseSend(returnMsg,map);
			if(distributedLock != null){
				distributedLock.unlock();
			}
			LOGGER.debug("批量结算回执报文:[{}]",returnMsg);
			LOGGER.info("---------批量结算同步处理结束--------");
		}


    		
    }
    
	/**
     * 文件下载
     * @param reqFileUrl
     * @param sndBankId 
     * @throws Exception 
     */
	private String downLoadFileSFtp( String sndBankId,String reqFileUrl) {
		String localfilepath ="";
		try {
			ToParm toParmSC = new ToParm();
			toParmSC.setDataTyp("bathfe");
			ToParmMapResultHandler toParmMapResultHandler = new ToParmMapResultHandler();
			mybatisSqlSession =(SqlSession) SpringUtil.getBean("mybatisSqlSession");
			mybatisSqlSession.select("com.nxy.cpfsfe.mybatis.mapper.ToParmMapper.getConfig", toParmSC, toParmMapResultHandler);
			
			Map<String,String> configMap = toParmMapResultHandler.getConifg();
			String host = configMap.get("host");
			Integer port = Integer.parseInt(configMap.get("port"));
			String username = configMap.get("username");
			String password = Encry.decryptString(configMap.get("password"));
			Integer timeout =  Integer.parseInt(configMap.get("timeout"));
			String localPath = configMap.get("locaDownPath");
			String remotePath = configMap.get("remotePathS") + File.separator+sndBankId;
			String[] fileArray = {reqFileUrl};
			LOGGER.info("下载结算申请文件{}，MFE配置信息：\nIP={}\n端口={}\n用户={}\n密码={}\n超时时间={}ms\n远程文件路径={}\n本地文件保存路径={}",
					fileArray[0],host,port,username,configMap.get("password"),timeout,remotePath,localPath);
	        
	        SFTPTransfer sftp = new SFTPTransfer();
//	       
	        sftp.downloadFileList(host, port, username, password,timeout,"", "", localPath, remotePath, fileArray, new ArrayList<String>());
	        //需要解压调用此方法
//	        FileUtil.deTarFile(localPath + File.separator + fileArray[0],localPath);
	        localfilepath = localPath+File.separator+reqFileUrl;
		} catch (Exception e) {
			LOGGER.error(CpfsBizException.BAT_FTP_DOWN_FAIL_MSG,e);
			throw new BaseException(RespCd.NCPS1002,e,CpfsBizException.BAT_FTP_DOWN_FAIL_MSG);
		}

		return localfilepath;
	}

	

	/**
	 * 明细请求表登记

	 */
	private int  batchDetailInsertReq(List<TdBatchA> tdBatchAList) {
		int tdBatchAResult =0;
        if(tdBatchAList != null && tdBatchAList.size() >0){
    		//获取事务控制管理器	
    		DataSourceTransactionManager txManager = null;
    		//获取事务状态
    		TransactionStatus txStatus = null;
    		try {
    			txManager = (DataSourceTransactionManager)SpringUtil.getBean("mybatisTxManager");
    			txStatus = txManager.getTransaction(new DefaultTransactionDefinition());	
    	        mybatisSqlSession =(SqlSession) SpringUtil.getBean("mybatisSqlSession");
    			//入库批量入账明细表
    	        tdBatchAResult =insertTdBatchAs(tdBatchAList);
    			txManager.commit(txStatus);
    		}catch (DataAccessException e){
				LOGGER.error("插入数据失败:资金归集批量请求明细表入库失败");
				int a =((SQLException) e.getCause()).getErrorCode();
				DBErrorCdEnum dberr = DBErrorCdEnum.getEnum(a+"");
				throw new BaseException(dberr.getName(),e,dberr.getDesc());
			} catch (Throwable e) {
    			if(txManager!= null&&txStatus != null){
        			txManager.rollback(txStatus);
    			}
    			LOGGER.error("插入数据失败：批量请求明细表TdBatchA",e);
    			throw new BaseException(RespCd.NCPS5002,e,CpfsBizException.BAT_BDBATCHA_FAIL_MSG);
    		}
        }
		return tdBatchAResult;

		
	}
	/**
	 * 入库批量入账明细表
	*
	 * @return
	 */
	private int insertTdBatchAs(List<TdBatchA> tdBatchAList) {
		mybatisSqlSession =(SqlSession) SpringUtil.getBean("mybatisSqlSession");

		//插入数据	
        TdBatchAMapper tmAMapper = mybatisSqlSession.getMapper(TdBatchAMapper.class);
        int num = tdBatchAList.size();
        int resultNum = 0;
        if(num > 50){
        	int i = num/50;
        	//遍历50笔更新一次
        	for (int j = 0; j < i; j++) {
        		List<TdBatchA>  lists = new ArrayList<TdBatchA>(50);
        		lists = tdBatchAList.subList(0+50*j, (j+1)*50);
        		resultNum += tmAMapper.insertTdBatchAs(lists);
			}
        	List<TdBatchA>  lists = tdBatchAList.subList(i*50, num);
        	if(lists!= null && lists.size() >0){
            	resultNum += tmAMapper.insertTdBatchAs(lists);

        	}
        }else{
        	resultNum= tmAMapper.insertTdBatchAs(tdBatchAList);
        }
        return resultNum;
	}

	/**
	 * 入库批量请求登记表
	 * @param map
	 * @return
	 */
	private TeBatchReq insertTeBatchReq(Map<String, String> map) {
		TeBatchReq tReq = new TeBatchReq();
		try {
			//请求系统编号
			tReq.setReqSysId(map.get("/root/MsgHeader/SndSysId"));
			//请求系统时间
			Date reqD =DateUtil.str2Date(map.get("/root/MsgHeader/SndDt"), "yyyy-MM-dd HH:mm:ss");
			tReq.setReqD(reqD);
			//通讯流水号
			tReq.setCorrId(map.get("/root/MsgHeader/CorrId"));
			//交易流水号
			tReq.setTrxId(map.get("/root/MsgHeader/TrxId"));	
			Date nowDate = new Date();
			//前置日期
			tReq.setCpfsD(nowDate);
			//请求文件地址
			tReq.setReqFileUrl(map.get("/root/MsgBody/FileNm"));
			//批次号
			tReq.setBatNo(map.get("/root/MsgBody/StlSumInfo/BatNo"));
			//一级商户号
			tReq.setMerId(map.get("/root/MsgBody/StlSumInfo/MerId"));
			//结算日期
			Date stlD =DateUtil.str2Date(map.get("/root/MsgBody/StlSumInfo/StlD"), "yyyyMMdd");
			tReq.setStlD(stlD);
			//待结算总笔数
			tReq.setbStlTotDN(Integer.valueOf(map.get("/root/MsgBody/StlSumInfo/BStlTotDN")));
			//待结算总金额
			tReq.setbStlTotAmt(new BigDecimal(map.get("/root/MsgBody/StlSumInfo/BStlTotAmt")));
			tReq.setStlTotDN(0);
			//批次处理状态0-待处理 1-处理中 2 处理完成 3-处理失败
			tReq.setProcSta(TeBatchReqEnum.ProcSta_Init.getCode());
			//批次处理状态描述
			//创建日期时间
			tReq.setCreTm(nowDate);
			//处理完成日期时间
			tReq.setProcTm(nowDate);
			tReq.setClbckUrl(map.get("/root/MsgHeader/ClbckUrl"));
			//插入数据
			map.put("procSta", TeBatchReqEnum.ProcSta_Init.getCode());
			mybatisSqlSession =(SqlSession) SpringUtil.getBean("mybatisSqlSession");
			TeBatchReqMapper tMapper = mybatisSqlSession.getMapper(TeBatchReqMapper.class);
			int num =tMapper.insertTeBatchReq(tReq);
			if(num != 1){
				throw new BaseException(RespCd.NCPS5002,"数据库更新数为0");
			}
		}catch (DataAccessException e){
			LOGGER.error("插入数据失败:资金归集批量请求表入库失败");
			int a =((SQLException) e.getCause()).getErrorCode();
			DBErrorCdEnum dberr = DBErrorCdEnum.getEnum(a+"");
			throw new BaseException(dberr.getName(),e,dberr.getDesc());
		} catch (Exception e) {
			LOGGER.error("插入数据失败：批量请求登记表teBatchReq",e);
			throw new BaseException(RespCd.NCPS5002,e,CpfsBizException.BAT_BEBATCHREQ_FAIL_MSG);
		}
		return tReq;

	}
	
	/**
	 * 文件内容检查
	 * 1 检查报文中的信息,文件信息是否为空，与汇总信息是否一致
	 * 2 汇总中的总明细与总金额是否与明细中的金额和笔数总额是否一致
	 * 3 汇总与明细字段是否正确 
	 * @param datalists
	 * @param tReq
	 * @param tALists
	 * @param map
	 * @return
	 */
	private void checkLists(List<String> datalists, TeBatchReq tReq, List<TdBatchA> tALists, Map<String, String> map) {
		String msg ="";
		if(datalists==null){
			msg ="文件内容为空";
		}else if(datalists.size() <2){
			msg ="文件内容行数不正确";
		}else{
			//获取汇总信息进行校验
			String[] sumline = datalists.get(0).split("\\|",7);

			if(sumline.length != 7){
				msg ="汇总行信息不正确";
			}else{
				//判定报文中的信息与汇总信息是否一致	
				if(!StringUtils.equals(tReq.getBatNo(), StringUtils.trim(sumline[0]))){
					msg ="请求报文与请求文件中的结算批次号信息不匹配";
//				}
//				else if(!StringUtils.equals(tReq.getMerId(), StringUtils.trim(sumline[1]))){
//					msg ="请求报文与请求文件中的一级商户号信息不匹配";
				}else if(!StringUtils.equals(DateUtil.date4Str(tReq.getStlD()), StringUtils.trim(sumline[2]))){
					msg ="请求报文与请求文件中的结算日期不匹配";
				}else if((tReq.getbStlTotDN().compareTo(Integer.valueOf(StringUtils.trim(sumline[3])))) !=0){
					LOGGER.error("请求报文中的笔数={},请求文件中的待结算总笔数:{}",tReq.getbStlTotDN(),Integer.valueOf(StringUtils.trim(sumline[3])) );
					msg ="请求报文与请求文件中的待结算总笔数不匹配";
				}else if(tReq.getbStlTotAmt().compareTo(new BigDecimal(StringUtils.trim(sumline[4])))!=0){
					msg ="请求报文与请求文件中的待结算总金额不匹配";
//				}else if(!StringUtils.equals(DateUtil.date4Str(tReq.getStlD()), StringUtils.trim(sumline[5]))){
//					msg ="请求报文与请求文件中的文件生成日期不匹配";
				}else if(tReq.getbStlTotDN().compareTo(Integer.valueOf(datalists.size()-1))!=0){
					LOGGER.error("汇总信息中的笔数={},文件明细中的笔数:{}",tReq.getbStlTotDN(),datalists.size()-1 );
					msg ="汇总信息中的笔数与文件明细中的笔数不匹配";
				}
			}
			
		}
		//解析明细信息
		if(StringUtils.isBlank(msg)){

			Date nowDate = new Date();
			//汇总金额信息
			BigDecimal sumAmt = BigDecimal.ZERO;
			Map<String,ToOrg> bankMap = new HashMap<String,ToOrg>();
			for (int i = 1; i < datalists.size(); i++) {
				String[] details = datalists.get(i).split("\\|",18);
				if(details.length != 18){
					msg ="请求文件终中的第["+i+"]明细数据异常";
					throw new BaseException(RespCd.NCPS1006, msg);
				}else{
					mybatisSqlSession =(SqlSession) SpringUtil.getBean("mybatisSqlSession");
					//插入数据
					ToOrgMapper toOrgMapper = mybatisSqlSession.getMapper(ToOrgMapper.class);
					//遍历获取所有的收单行
					List<ToOrg> list = toOrgMapper.queryRecBank(new ToOrg());
					for (ToOrg toOrg : list){
						bankMap.put(toOrg.getOrgBNo(),toOrg);
					}
					ToOrg toOrgPyr = bankMap.get(StringUtil.trimObject(details[6]));
					ToOrg toOrgPye = bankMap.get(StringUtil.trimObject(details[10]));
					if(null == toOrgPyr || null == toOrgPye ){
						throw new BaseException(RespCd.NCPS9001, "发送行|接收行存在非收单行");
					}
					TdBatchA tdBatchA = new TdBatchA();
//					tdBatchA.setMsgId(details[0]);
					tdBatchA.setBatNo(tReq.getBatNo());tdBatchA.setReqSysId(tReq.getReqSysId());
					tdBatchA.setSeqNo(details[0]); tdBatchA.setxDir(details[1]); tdBatchA.setOrdNo(details[2]);tdBatchA.setpTyp(details[3]);
				    tdBatchA.setMerId(details[4]); tdBatchA.setMerId2(details[5]); tdBatchA.setPyrBNo(details[6]); 
				    tdBatchA.setPyrBNm(details[7]);tdBatchA.setPyrA(details[8]);tdBatchA.setPyrNm(details[9]);
				    tdBatchA.setPyeBNo(details[10]); tdBatchA.setPyeBNm(details[11]); tdBatchA.setPyeA(details[12]);
				    //明细金额叠加
					BigDecimal detailAmt = new BigDecimal(details[14]);
					sumAmt =sumAmt.add(detailAmt);
				    tdBatchA.setPyeNm(details[13]);
				    tdBatchA.setxAmt(detailAmt);
				    if(StringUtils.isNotEmpty(details[15])){
						tdBatchA.setYrAmt(new BigDecimal(details[15]));
					}
					if(StringUtils.isNotEmpty(details[16])){
						tdBatchA.setxAmt2(new BigDecimal(details[16]));
					}
				    tdBatchA.setMerRmk(details[17]);
				    tdBatchA.setStlSeqNo(tReq.getTrxId());
					if(tdBatchA.getPyrBNo().equals(tdBatchA.getPyeBNo())){
						tdBatchA.setStlSta(TdBatchAEnum.StlSta_Succ.getCode());
					}else{
						tdBatchA.setStlSta(TdBatchAEnum.StlSta_Init.getCode());
					}
				    tdBatchA.setCreTm(nowDate);
//				    tdBatchA.setUpdTm(nowDate);
					tALists.add(tdBatchA);
				}
			}
			if(sumAmt.compareTo(tReq.getbStlTotAmt())!= 0){
				msg ="汇总信息中的金额与文件明细中的总金额不匹配";
				 throw new BaseException(RespCd.NCPO6652, msg);
			}
		}
		
		if(StringUtils.isNotBlank(msg)){
			 throw new BaseException(RespCd.NCPS1006, msg);
		}

	}


	 /**
     * 报文内容检查，目前只检查是否为空
     * @param map
     * @return
     */
	private void bizMsgXmlCheck(Map<String, String> map) {
		String resultMsg ="";
		if(StringUtils.isBlank(map.get("/root/MsgBody/FileNm"))){
			resultMsg = "文件路径为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgBody/StlSumInfo/BatNo"))){
			resultMsg = "结算批次号为空";
//		}
//		else if(StringUtils.isBlank(map.get("/root/MsgBody/StlSumInfo/MerId")))
//		{
//			resultMsg = "一级商户号为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgBody/StlSumInfo/StlD"))){
			resultMsg = "结算日期为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgBody/StlSumInfo/BStlTotDN"))){
			resultMsg = "待结算总笔数为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgBody/StlSumInfo/BStlTotAmt"))){
			resultMsg = "待结算总金额为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgBody/StlSumInfo/FileCreD"))){
			resultMsg = "文件生成日期为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgBody/StlSumInfo/FileCreT"))){
			resultMsg = "文件生成时间为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgHeader/ClbckUrl"))){
			resultMsg = "异步通知URL为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgHeader/SndBankId"))){
			resultMsg = "发起方行号为空";
		}else if(StringUtils.isBlank(map.get("/root/MsgHeader/SndSysId"))){
			resultMsg = "业务系统编号为空";
		}
		if(StringUtils.isNotEmpty(resultMsg)){
			throw new BaseException(RespCd.NCPS2400, resultMsg);
		}
	}
	

}
